﻿using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace YYFRedis
{
    public class RedisCacheProvider
    {
        /// <summary>
        /// 连接字符串
        /// </summary>
        public static string RedisConnStr { get; set; }
        /// <summary>
        /// 默认的 Key 值（用来当作 RedisKey 的前缀）
        /// </summary>
        public static string PrefixName { get; set; }
        /// <summary>
        /// Redis 连接对象
        /// </summary>
        private readonly ConnectionMultiplexer _connectionMultiplexer;
        /// <summary>
        /// 数据库
        /// </summary>
        private readonly IDatabase _database;
        /// <summary>
        /// 集群服务器
        /// </summary>
        private readonly IEnumerable<IServer> _servers;

        public string Name { get; }

        public bool IsDistributedCache => false;

        /// <summary>
        /// 统计缓存器
        /// </summary>
        public CacheStatsInfo CacheStatsInfo { get; }

        public static IServiceProvider Instance { get; set; }

        public int MyProperty { get; set; }

        /// <summary>
        /// 锁
        /// </summary>
        private static readonly object Locker = new object();

        /// <summary>
        /// 构造函数
        /// </summary>
        public RedisCacheProvider()
        {
            CacheStatsInfo = new CacheStatsInfo();
            if (_connectionMultiplexer == null && !string.IsNullOrWhiteSpace(RedisConnStr))
            {
                _connectionMultiplexer = ConnectionMultiplexer.Connect(RedisConnStr);
            }
            if (_connectionMultiplexer != null)
            {
                _database = GetDatabase();
                _servers = GetServerList();
            }
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="connstr">Redis连接字符串</param>
        public RedisCacheProvider(string connstr)
        {
            CacheStatsInfo = new CacheStatsInfo();
            if (_connectionMultiplexer == null && !string.IsNullOrWhiteSpace(connstr))
            {
                _connectionMultiplexer = ConnectionMultiplexer.Connect(connstr);
            }
            if (_connectionMultiplexer != null)
            {
                _database = GetDatabase();
                _servers = GetServerList();
            }
        }

        public IDatabase GetDatabase()
        {
            return _connectionMultiplexer.GetDatabase();
        }
        /// <summary>
        /// 获取集群服务器列表
        /// </summary>
        /// <returns></returns>
        public IEnumerable<IServer> GetServerList()
        {
            List<EndPoint> mastersServersEndpoints = GetMastersServersEndpoints();
            foreach (EndPoint item in mastersServersEndpoints)
            {
                yield return _connectionMultiplexer.GetServer(item);
            }
        }
        /// <summary>
        /// 获取集群主节点
        /// </summary>
        /// <returns></returns>
        private List<EndPoint> GetMastersServersEndpoints()
        {
            List<EndPoint> list = new List<EndPoint>();
            EndPoint[] endPoints = _connectionMultiplexer.GetEndPoints();
            foreach (EndPoint endPoint in endPoints)
            {
                IServer server = _connectionMultiplexer.GetServer(endPoint);
                if (!server.IsConnected)
                {
                    continue;
                }

                if (server.ServerType == ServerType.Cluster)
                {
                    list.AddRange(from p in server.ClusterConfiguration.Nodes
                                  where !p.IsSlave
                                  select p.EndPoint);
                    break;
                }

                if (server.ServerType == ServerType.Standalone && !server.IsSlave)
                {
                    list.Add(endPoint);
                    break;
                }
            }

            return list;
        }
        /// <summary>
        /// 获取数量
        /// </summary>
        /// <param name="prefix">前缀</param>
        /// <returns></returns>
        public int GetCount(string prefix = "")
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                int num = 0;
                {
                    foreach (IServer server in _servers)
                    {
                        num += (int)server.DatabaseSize(_database.Database);
                    }

                    return num;
                }
            }

            return SearchRedisKeys(prefix).Length;
        }
        /// <summary>
        /// 键是否存在
        /// </summary>
        /// <param name="cacheKey">键</param>
        /// <returns></returns>
        public bool Exists(string cacheKey)
        {
            if (string.IsNullOrWhiteSpace(cacheKey))
            {
                return false;
            }

            cacheKey = PrefixKey(cacheKey);
            return _database.KeyExists(cacheKey);
        }
        /// <summary>
        /// 键是否存在（异步）
        /// </summary>
        /// <param name="cacheKey">键</param>
        /// <returns></returns>
        public async Task<bool> ExistsAsync(string cacheKey)
        {
            if (string.IsNullOrWhiteSpace(cacheKey))
            {
                return false;
            }

            cacheKey = PrefixKey(cacheKey);
            return await _database.KeyExistsAsync(cacheKey);
        }
        /// <summary>
        /// 键是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public bool KeyExists(RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return _database.KeyExists(key, flags);
        }
        /// <summary>
        /// 通过前缀获取所有字段值
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public RedisKey[] SearchRedisKeys(string pattern)
        {
            List<RedisKey> list = new List<RedisKey>();
            foreach (IServer server in _servers)
            {
                list.AddRange(server.Keys(pattern: pattern, database: _database.Database, pageSize: 10, cursor: 0L));
            }

            return list.Distinct().ToArray();
        }

        public IDatabase GetConn()
        {
            return _database;
        }
        #region String 操作

        public CacheValue<string> StringGet(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                RedisValue redisValue = _database.StringGet(cacheKey);
                if (!redisValue.IsNull)
                {
                    return new CacheValue<string>(redisValue, hasvalue: true);
                }

                CacheStatsInfo.OnMiss();
            }

            return CacheValue<string>.NoValue;
        }
        public RedisValue StringGet(RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return _database.StringGet(key, flags);
        }

        public async Task<CacheValue<string>> StringGetAsync(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                RedisValue redisValue = await _database.StringGetAsync(cacheKey);
                if (!redisValue.IsNull)
                {
                    return new CacheValue<string>(redisValue, hasvalue: true);
                }

                CacheStatsInfo.OnMiss();
            }

            return CacheValue<string>.NoValue;
        }
        public async Task<RedisValue> StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            return await _database.StringGetAsync(key, flags);
        }

        public async Task StringSetAsync(string cacheKey, string cacheValue, TimeSpan? expiry = null)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && !string.IsNullOrWhiteSpace(cacheValue) && (!expiry.HasValue || expiry >= TimeSpan.Zero))
            {
                cacheKey = PrefixKey(cacheKey);
                await _database.StringSetAsync(cacheKey, cacheValue, expiry);
            }
        }

        public void StringSet(string cacheKey, string cacheValue, TimeSpan? expiry = null)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && !string.IsNullOrWhiteSpace(cacheValue) && (!expiry.HasValue || expiry >= TimeSpan.Zero))
            {
                cacheKey = PrefixKey(cacheKey);
                _database.StringSet(cacheKey, cacheValue, expiry);
            }
        }
        public bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags)
        {
            return _database.StringSet(key, value, expiry, when, flags);
        }
        public async Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags)
        {
            return await _database.StringSetAsync(key, value, expiry, when, flags);
        }

        public double StringSetIncrement(string redisKey, double value)
        {
            return _database.StringIncrement(redisKey, value);
        }

        public async Task<double> StringSetIncrementAsync(string redisKey, double value)
        {
            return await _database.StringIncrementAsync(redisKey, value);
        }
        #endregion

        #region 泛型操作
        public CacheValue<T> Get<T>(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                RedisValue redisValue = _database.StringGet(cacheKey);
                if (!redisValue.IsNull)
                {
                    CacheStatsInfo.OnHit();
                    return new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true);
                }

                CacheStatsInfo.OnMiss();
            }

            return CacheValue<T>.NoValue;
        }

        public async Task<CacheValue<T>> GetAsync<T>(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                RedisValue redisValue = await _database.StringGetAsync(cacheKey);
                if (!redisValue.IsNull)
                {
                    CacheStatsInfo.OnHit();
                    return new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true);
                }

                CacheStatsInfo.OnMiss();
            }

            return CacheValue<T>.NoValue;
        }

        public CacheValue<T> Get<T>(string cacheKey, Func<T> dataRetriever, TimeSpan expiry) where T : class
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && expiry > TimeSpan.Zero)
            {
                cacheKey = PrefixKey(cacheKey);
                RedisValue redisValue = _database.StringGet(cacheKey);
                if (!redisValue.IsNull)
                {
                    CacheStatsInfo.OnHit();
                    return new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true);
                }

                T val = ((dataRetriever != null) ? dataRetriever() : null);
                if (val != null)
                {
                    Set(cacheKey, val, expiry);
                    return new CacheValue<T>(val, hasvalue: true);
                }
            }

            return CacheValue<T>.NoValue;
        }

        public async Task<CacheValue<T>> GetAsync<T>(string cacheKey, Func<Task<T>> dataRetriever, TimeSpan expiry) where T : class
        {
            cacheKey = PrefixKey(cacheKey);
            if (!string.IsNullOrWhiteSpace(cacheKey) && expiry > TimeSpan.Zero)
            {
                RedisValue redisValue = await _database.StringGetAsync(cacheKey);
                if (!redisValue.IsNull)
                {
                    CacheStatsInfo.OnHit();
                    return new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true);
                }

                T item = await (dataRetriever?.Invoke());
                if (item != null)
                {
                    await SetAsync(cacheKey, item, expiry);
                    return new CacheValue<T>(item, hasvalue: true);
                }
            }

            return CacheValue<T>.NoValue;
        }

        public IDictionary<string, CacheValue<T>> GetAll<T>(IEnumerable<string> cacheKeys)
        {
            Dictionary<string, CacheValue<T>> dictionary = new Dictionary<string, CacheValue<T>>();
            if (cacheKeys.Any() && !cacheKeys.Any((string p) => string.IsNullOrWhiteSpace(p)))
            {
                cacheKeys = cacheKeys.Select((string p) => PrefixKey(p));
                string[] array = cacheKeys.ToArray();
                RedisValue[] array2 = _database.StringGet(((IEnumerable<string>)array).Select((Func<string, RedisKey>)((string x) => x)).ToArray());
                for (int i = 0; i < array.Length; i++)
                {
                    RedisValue redisValue = array2[i];
                    if (!redisValue.IsNull)
                    {
                        dictionary.Add(array[i], new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true));
                    }
                    else
                    {
                        dictionary.Add(array[i], CacheValue<T>.NoValue);
                    }
                }
            }

            return dictionary;
        }

        public async Task<IDictionary<string, CacheValue<T>>> GetAllAsync<T>(IEnumerable<string> cacheKeys)
        {
            Dictionary<string, CacheValue<T>> result = new Dictionary<string, CacheValue<T>>();
            if (cacheKeys.Any() && !cacheKeys.Any((string p) => string.IsNullOrWhiteSpace(p)))
            {
                cacheKeys = cacheKeys.Select((string p) => PrefixKey(p));
                string[] keyArray = cacheKeys.ToArray();
                RedisValue[] array = await _database.StringGetAsync(((IEnumerable<string>)keyArray).Select((Func<string, RedisKey>)((string x) => x)).ToArray());
                for (int i = 0; i < keyArray.Length; i++)
                {
                    RedisValue redisValue = array[i];
                    if (!redisValue.IsNull)
                    {
                        result.Add(keyArray[i], new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true));
                    }
                    else
                    {
                        result.Add(keyArray[i], CacheValue<T>.NoValue);
                    }
                }
            }

            return result;
        }

        public IDictionary<string, CacheValue<T>> GetByPrefix<T>(string prefix, string excludeStr = "")
        {
            Dictionary<string, CacheValue<T>> dictionary = new Dictionary<string, CacheValue<T>>();
            if (!string.IsNullOrWhiteSpace(prefix))
            {
                prefix = HandlerPrefix(prefix);
                RedisKey[] array = SearchRedisKeys(prefix);
                if (!string.IsNullOrWhiteSpace(excludeStr))
                {
                    array = array.Where((RedisKey p) => !p.ToString().Contains(excludeStr)).ToArray();
                }

                RedisValue[] array2 = _database.StringGet(array).ToArray();
                for (int i = 0; i < array.Length; i++)
                {
                    RedisValue redisValue = array2[i];
                    if (!redisValue.IsNull)
                    {
                        dictionary.Add(array[i], new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true));
                    }
                    else
                    {
                        dictionary.Add(array[i], CacheValue<T>.NoValue);
                    }
                }
            }

            return dictionary;
        }

        public async Task<IDictionary<string, CacheValue<T>>> GetByPrefixAsync<T>(string prefix, string excludeStr)
        {
            Dictionary<string, CacheValue<T>> result = new Dictionary<string, CacheValue<T>>();
            if (!string.IsNullOrWhiteSpace(prefix))
            {
                prefix = HandlerPrefix(prefix);
                RedisKey[] redisKeys = SearchRedisKeys(prefix);
                if (!string.IsNullOrWhiteSpace(excludeStr))
                {
                    redisKeys = redisKeys.Where((RedisKey p) => !p.ToString().Contains(excludeStr)).ToArray();
                }

                RedisValue[] array = (await _database.StringGetAsync(redisKeys)).ToArray();
                for (int i = 0; i < redisKeys.Length; i++)
                {
                    RedisValue redisValue = array[i];
                    if (!redisValue.IsNull)
                    {
                        result.Add(redisKeys[i], new CacheValue<T>(Newtonsoft.Json.JsonConvert.DeserializeObject<T>(redisValue), hasvalue: true));
                    }
                    else
                    {
                        result.Add(redisKeys[i], CacheValue<T>.NoValue);
                    }
                }
            }

            return result;
        }

        public void Set<T>(string cacheKey, T cacheValue, TimeSpan? expiry)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null)
            {
                cacheKey = PrefixKey(cacheKey);
                _database.StringSet(cacheKey, Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue), expiry);
            }
        }
        public void SetValue<T>(string cacheKey, T cacheValue, TimeSpan? expiry, bool tojson = true)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null)
            {
                cacheKey = PrefixKey(cacheKey);
                var value = cacheValue.ToString();
                if (tojson)
                {
                    value = Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue);
                }
                _database.StringSet(cacheKey, value, expiry);
            }
        }

        public async Task SetAsync<T>(string cacheKey, T cacheValue, TimeSpan? expiry)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null)
            {
                cacheKey = PrefixKey(cacheKey);
                await _database.StringSetAsync(cacheKey, Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue), expiry);
            }
        }
        public async Task SetValueAsync<T>(string cacheKey, T cacheValue, TimeSpan? expiry, bool tojson = true)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null)
            {
                cacheKey = PrefixKey(cacheKey);
                var value = cacheValue.ToString();
                if (tojson)
                {
                    value = Newtonsoft.Json.JsonConvert.SerializeObject(cacheValue);
                }
                await _database.StringSetAsync(cacheKey, value, expiry);
            }
        }

        public void SetAll<T>(IDictionary<string, T> values, TimeSpan expiry)
        {
            if (values == null || values.Any() || !(expiry > TimeSpan.Zero))
            {
                return;
            }

            IBatch batch = _database.CreateBatch();
            foreach (KeyValuePair<string, T> value in values)
            {
                batch.StringSetAsync(PrefixKey(value.Key), Newtonsoft.Json.JsonConvert.SerializeObject(value.Value), expiry);
            }

            batch.Execute();
        }

        public async Task SetAllAsync<T>(IDictionary<string, T> values, TimeSpan expiry)
        {
            if (values == null || values.Any() || !(expiry > TimeSpan.Zero))
            {
                return;
            }

            List<Task> list = new List<Task>();
            foreach (KeyValuePair<string, T> value in values)
            {
                list.Add(SetAsync(PrefixKey(value.Key), value.Value, expiry));
            }

            await Task.WhenAll(list);
        }
        #endregion

        #region 移除操作

        public void Remove(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                _database.KeyDelete(cacheKey);
            }
        }
        public void KeyDelete(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                _database.KeyDelete(cacheKey);
            }
        }
        public void KeyDelete(RedisKey key, CommandFlags flags = CommandFlags.None)
        {
            _database.KeyDelete(key, flags);
        }
        public void KeyDelete(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
        {
            _database.KeyDelete(keys, flags);
        }

        public async Task RemoveAsync(string cacheKey)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                cacheKey = PrefixKey(cacheKey);
                await _database.KeyDeleteAsync(cacheKey);
            }
        }
        public void KeyDeleteAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
        {
            _database.KeyDeleteAsync(keys, flags);
        }

        public void RemoveByPrefix(string prefix)
        {
            if (!string.IsNullOrWhiteSpace(prefix))
            {
                prefix = HandlerPrefix(prefix);
                RedisKey[] keys = SearchRedisKeys(prefix);
                _database.KeyDelete(keys);
            }
        }

        public async Task RemoveByPrefixAsync(string prefix)
        {
            if (!string.IsNullOrWhiteSpace(prefix))
            {
                prefix = HandlerPrefix(prefix);
                RedisKey[] keys = SearchRedisKeys(prefix);
                await _database.KeyDeleteAsync(keys);
            }
        }

        public void RemoveAll(IEnumerable<string> cacheKeys)
        {
            if (cacheKeys != null && cacheKeys.Any())
            {
                RedisKey[] array = cacheKeys.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select((Func<string, RedisKey>)((string p) => PrefixKey(p))).ToArray();
                if (array.Length != 0)
                {
                    _database.KeyDelete(array);
                }
            }
        }

        public async Task RemoveAllAsync(IEnumerable<string> cacheKeys)
        {
            if (cacheKeys != null && cacheKeys.Any())
            {
                RedisKey[] array = cacheKeys.Where((string x) => !string.IsNullOrWhiteSpace(x)).Select((Func<string, RedisKey>)((string p) => PrefixKey(p))).ToArray();
                if (array.Length != 0)
                {
                    await _database.KeyDeleteAsync(array);
                }
            }
        }
        #endregion

        public void Refresh<T>(string cacheKey, T cacheValue, TimeSpan expiry)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null && expiry > TimeSpan.Zero)
            {
                Remove(cacheKey);
                Set(cacheKey, cacheValue, expiry);
            }
        }

        public async Task RefreshAsync<T>(string cacheKey, T cacheValue, TimeSpan expiry)
        {
            if (!string.IsNullOrWhiteSpace(cacheKey) && cacheValue != null && expiry > TimeSpan.Zero)
            {
                await RemoveAsync(cacheKey);
                await SetAsync(cacheKey, cacheValue, expiry);
            }
        }

        public void Flush()
        {
            foreach (IServer server in _servers)
            {
                server.FlushDatabase(_database.Database);
            }
        }

        public async Task FlushAsync()
        {
            List<Task> list = new List<Task>();
            foreach (IServer server in _servers)
            {
                list.Add(server.FlushDatabaseAsync(_database.Database));
            }

            await Task.WhenAll(list);
        }

        #region 有序集合
        public double? SortedSetGetScored(string redisKey, string member, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SortedSetScore(redisKey, member);
        }

        public async Task<double?> SortedSetGetScoredAsync(string redisKey, string member, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SortedSetScoreAsync(redisKey, member);
        }

        public IEnumerable<string> SortedSetRangeByRank(string redisKey, long start = 0L, long stop = -1L, int order = 1, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return from x in _database.SortedSetRangeByRank(redisKey, start, stop, (Order)order)
                   select x.ToString();
        }

        public async Task<IEnumerable<string>> SortedSetRangeByRankAsync(string redisKey, long start = 0L, long stop = -1L, int order = 1, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return (await _database.SortedSetRangeByRankAsync(redisKey, start, stop, (Order)order)).Select((RedisValue x) => x.ToString());
        }

        public bool SortedSetAdd(string redisKey, string member, double score, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SortedSetAdd(redisKey, member, score);
        }

        public async Task<bool> SortedSetAddAsync(string redisKey, string member, double score, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SortedSetAddAsync(redisKey, member, score);
        }

        public Dictionary<string, double> GetSortedWithScore(string cacheKey, long start = 0L, long end = 0L, Order order = StackExchange.Redis.Order.Descending, bool withPefixkey = false)
        {
            Dictionary<string, double> result = new Dictionary<string, double>();
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                if (withPefixkey)
                {
                    cacheKey = PrefixKey(cacheKey);
                }

                SortedSetEntry[] array = _database.SortedSetRangeByRankWithScores(cacheKey, start, end, order);
                if (array != null)
                {
                    result = array.ToStringDictionary();
                }
            }

            return result;
        }

        public async Task<Dictionary<string, double>> GetSortedWithScoreAsync(string cacheKey, long start = 0L, long end = -1L, Order order = StackExchange.Redis.Order.Descending, bool withPefixkey = false)
        {
            Dictionary<string, double> result = new Dictionary<string, double>();
            if (!string.IsNullOrWhiteSpace(cacheKey))
            {
                if (withPefixkey)
                {
                    cacheKey = PrefixKey(cacheKey);
                }

                SortedSetEntry[] array = await _database.SortedSetRangeByRankWithScoresAsync(cacheKey, start, end, order);
                if (array != null)
                {
                    result = array.ToStringDictionary();
                }
            }

            return result;
        }

        public long SortedSetLength(string redisKey, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SortedSetLength(redisKey);
        }

        public bool SortedSetRemoveMember(string redisKey, string member, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SortedSetRemove(redisKey, member);
        }

        public async Task<bool> SortedSetRemoveMemberAsync(string redisKey, string member, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SortedSetRemoveAsync(redisKey, member);
        }

        public double SortedSetIncrement(string redisKey, string member, double value = 1.0, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return 0.0;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SortedSetIncrement(redisKey, member, value);
        }

        public async Task<double> SortedSetIncrementAsync(string redisKey, string member, double value = 1.0, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(member))
            {
                return 0.0;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SortedSetIncrementAsync(redisKey, member, value);
        }
        #endregion

        #region Hash 操作
        /// <summary>
        /// 从 hash 返回所有的字段值
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public Dictionary<RedisValue, RedisValue> HashGetAll(string redisKey, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return new Dictionary<RedisValue, RedisValue>();
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashGetAll(redisKey).ToDictionary();
        }
        /// <summary>
        /// 从 hash 异步返回所有的字段值
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public async Task<Dictionary<RedisValue, RedisValue>> HashGetAllAsync(string redisKey, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return new Dictionary<RedisValue, RedisValue>();
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            _database.HashGetAll(redisKey).ToDictionary();
            return (await _database.HashGetAllAsync(redisKey)).ToDictionary();
        }
        /// <summary>
        /// 获取hash的数量
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public long HashLength(string redisKey, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashLength(redisKey);
        }
        /// <summary>
        /// 异步获取hash的数量
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public async Task<long> HashLengthAsync(string redisKey, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.HashLengthAsync(redisKey);
        }

        /// <summary>
        /// 在 hash 设定值
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="hashField"></param>
        /// <param name="value"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public bool HashSet(string redisKey, string hashField, string value, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashSet(redisKey, hashField, value);
        }
        /// <summary>
        /// 在 hash 异步设定值
        /// </summary>
        /// <param name="redisKey"></param>
        /// <param name="hashField"></param>
        /// <param name="value"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public async Task<bool> HashSetAsync(string redisKey, string hashField, string value, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.HashSetAsync(redisKey, hashField, value);
        }

        public long HashValueIncrement(string redisKey, string hashField, long value, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashIncrement(redisKey, hashField, value);
        }

        public async Task<long> HashValueIncrementAsync(string redisKey, string hashField, long value, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.HashIncrementAsync(redisKey, hashField, value);
        }

        public bool HashExists(string redisKey, string hashField, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashExists(redisKey, hashField);
        }

        public async Task<bool> HashExistsAsync(string redisKey, string hashField, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.HashExistsAsync(redisKey, hashField);
        }

        public async Task<bool> HashRemoveMemberAsync(string redisKey, string hashField, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.HashDeleteAsync(redisKey, hashField);
        }

        public bool HashRemoveMember(string redisKey, string hashField, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.HashDelete(redisKey, hashField);
        }
        #endregion

        #region Key 操作
        public async Task<bool> SetExistAsync(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SetContainsAsync(redisKey, value);
        }

        public bool SetExist(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SetContains(redisKey, value);
        }

        public async Task<IEnumerable<string>> SetGetAllAsync(string redisKey, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return (await _database.SetMembersAsync(redisKey)).Select((RedisValue p) => p.ToString());
        }

        public IEnumerable<string> SetGetAll(string redisKey, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return null;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return from p in _database.SetMembers(redisKey)
                   select p.ToString();
        }

        public async Task<bool> SetAddAsync(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SetAddAsync(redisKey, value);
        }

        public bool SetAdd(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SetAdd(redisKey, value);
        }

        public bool SetRmoveMember(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SetRemove(redisKey, value);
        }

        public async Task<bool> SetRmoveMemberAsync(string redisKey, string value, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(value))
            {
                return false;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SetRemoveAsync(redisKey, value);
        }

        public long SetLength(string redisKey, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.SetLength(redisKey);
        }

        public async Task<long> SetLengthAsync(string redisKey, bool userDefaultKey = false)
        {
            if (string.IsNullOrWhiteSpace(redisKey))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return await _database.SetLengthAsync(redisKey);
        }
        #endregion


        #region 发布订阅

        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="handle"></param>
        public void Subscribe(RedisChannel channel, Action<RedisChannel, RedisValue> handle)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            sub.Subscribe(channel, handle);
        }

        /// <summary>
        /// 发布
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public long Publish(RedisChannel channel, RedisValue message)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            return sub.Publish(channel, message);
        }
        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public void Unsubscribe(RedisChannel channel)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            sub.Unsubscribe(channel);
        }
        /// <summary>
        /// 取消全部订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public void UnsubscribeAll()
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            sub.UnsubscribeAll();
        }
        #region 发布订阅-async

        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="handle"></param>
        public async Task SubscribeAsync(RedisChannel channel, Action<RedisChannel, RedisValue> handle)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            await sub.SubscribeAsync(channel, handle);
        }

        /// <summary>
        /// 发布
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task<long> PublishAsync(RedisChannel channel, RedisValue message)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            return await sub.PublishAsync(channel, message);
        }
        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public void UnsubscribeAsync(RedisChannel channel)
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            sub.UnsubscribeAsync(channel);
        }
        /// <summary>
        /// 取消全部订阅
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public void UnsubscribeAllAsync()
        {
            var sub = _connectionMultiplexer.GetSubscriber();
            sub.UnsubscribeAllAsync();
        }
        #endregion 发布订阅-async
        #endregion 发布订阅

        #region 消息队列
        /// <summary>
        /// 生产者(发消息) 
        /// </summary>
        /// <param name="redisKey">队列主键</param>
        /// <param name="redisValue"></param>
        /// <param name="userDefaultKey"></param>
        /// <returns></returns>
        public long ListRightPush(string redisKey, string redisValue, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(redisKey) || string.IsNullOrWhiteSpace(redisValue))
            {
                return 0L;
            }

            redisKey = (userDefaultKey ? PrefixKey(redisKey) : redisKey);
            return _database.ListRightPush(redisKey, redisValue);
        }
        /// <summary>
        /// 消费者(收消息) 
        /// </summary>
        /// <param name="queuekey">队列主键</param>
        /// <param name="userDefaultKey">默认前缀</param>
        /// <returns></returns>
        public RedisValue ListLeftPop(string queuekey, bool userDefaultKey = true)
        {
            queuekey = (userDefaultKey ? PrefixKey(queuekey) : queuekey);
            return _database.ListLeftPop(queuekey);
        }
        /// <summary>
        /// 加入消息队列(发数据) 
        /// </summary>
        /// <param name="queuekey">队列主键</param>
        /// <param name="queuedata">队列数据</param>
        /// <param name="userDefaultKey">默认前缀</param>
        /// <returns>消息队列数据个数</returns>
        public long QueuePush(string queuekey, string queuedata, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(queuekey) || string.IsNullOrWhiteSpace(queuedata))
            {
                return 0L;
            }

            queuekey = (userDefaultKey ? PrefixKey(queuekey) : queuekey);
            return _database.ListRightPush(queuekey, queuedata);
        }
        /// <summary>
        /// 加入消息队列(发数据) 
        /// </summary>
        /// <param name="queuekey">队列主键</param>
        /// <param name="queuedata">队列数据</param>
        /// <param name="userDefaultKey">默认前缀</param>
        /// <returns>消息队列数据个数</returns>
        public long QueuePush(string queuekey, object queuedata, bool userDefaultKey = true)
        {
            if (string.IsNullOrWhiteSpace(queuekey) || queuedata == null)
            {
                return 0L;
            }

            queuekey = (userDefaultKey ? PrefixKey(queuekey) : queuekey);
            return _database.ListRightPush(queuekey, Newtonsoft.Json.JsonConvert.SerializeObject(queuedata));
        }
        /// <summary>
        /// 处理并移除消息队列(消费数据) 
        /// </summary>
        /// <param name="queuekey">队列主键</param>
        /// <param name="userDefaultKey">默认前缀</param>
        /// <returns></returns>
        public RedisValue QueuePop(string queuekey, bool userDefaultKey = true)
        {
            queuekey = (userDefaultKey ? PrefixKey(queuekey) : queuekey);
            return _database.ListLeftPop(queuekey);
        }
        #endregion

        private string HandlerPrefix(string prefix)
        {
            if (prefix.Trim().Equals("*"))
            {
                throw new ArgumentException("前缀不能等于 * ");
            }

            prefix = new Regex("^\\*+").Replace(prefix, "");
            if (!prefix.EndsWith("*", StringComparison.InvariantCultureIgnoreCase))
            {
                prefix += "*";
            }

            prefix = PrefixKey(prefix);
            return prefix;
        }

        private string PrefixKey(string key)
        {
            if (!string.IsNullOrWhiteSpace(PrefixName))
            {
                return $"{PrefixName}:{key}";
            }

            return key;
        }
    }
}
